package com.example.config;

import com.example.filter.JwtAuthenticationTokenFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * 从 Spring Security 5.7 开始，官方就明确标记 WebSecurityConfigurerAdapter 为 deprecated
 * 在 Spring Security 6（对应 Spring Boot 3.x） 中彻底移除了
 */

@Configuration
@EnableWebSecurity // 开启 Spring Security
@RequiredArgsConstructor
@EnableMethodSecurity(prePostEnabled = true) // 开启 Spring Security 的方法级安全
public class SecurityConfig {

    private final JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    /**
     * 认证管理器
     * ✅ 认证管理器的调用链中会自动调用密码编码
     * ✅ 确切位置：authenticationManager.authenticate(token) → DaoAuthenticationProvider.additionalAuthenticationChecks() → passwordEncoder.matches()
     *
     * @param authenticationConfiguration
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    /**
     * 密码编码器
     * Spring Security 在验证时会根据前缀自动选择合适的加密器
     * Spring Security 自带了一个默认的密码编码器：
     * DaoAuthenticationProvider 内部 默认就持有：
     * private PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
     * 这个默认值就是 DelegatingPasswordEncoder（委派编码器）。
     * 它在校验时会期望数据库中的密码形如 "{id}encoded"，因此会尝试解析前缀 {bcrypt}、{noop}、{pbkdf2}、{argon2}、{scrypt} 等。如果拿到的“已编码密码”没有合法前缀或为空/缺失，就会抛你看到的异常（堆栈里正是 DelegatingPasswordEncoder）。
     * 换句话说：即使你没有提供任何 PasswordEncoder Bean，框架也会启用内置的 DelegatingPasswordEncoder，所以你会看到对 {noop}（以及其他 {id}）前缀格式的检查。
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 认证失败处理
    private final AuthenticationEntryPoint authenticationEntryPoint;
    // 授权失败处理
    private final AccessDeniedHandler accessDeniedHandler;

    /**
     * 配置 Spring Security
     * Spring Security 6 已经废弃了 WebSecurityConfigurerAdapter，所以现在要显式声明一个 SecurityFilterChain Bean
     * 这个 Bean 就告诉 Spring Security：该项目的安全规则是什么，如何处理请求、认证、过滤
     *
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                /**
                 * 攻击模型：
                 * 用户在 A 网站已经登录（浏览器保存了 Cookie），此时访问了恶意的 B 网站。
                 * B 网站偷偷构造一个请求（POST/DELETE…）发向 A 网站的接口，因为浏览器会自动带上 Cookie，A 网站就以为这是合法用户的请求。
                 * 结果：黑客就利用了用户的身份在 A 网站上执行了敏感操作（转账、修改资料等）。
                 */
                .csrf(AbstractHttpConfigurer::disable) // 关闭 csrf CSRF（Cross-Site Request Forgery，跨站请求伪造）
                .cors(AbstractHttpConfigurer::disable) // 关闭 cors CORS（Cross-Origin Resource Sharing，跨域资源共享）
                .exceptionHandling(
                        exception -> exception
                                .authenticationEntryPoint(authenticationEntryPoint) // 认证失败处理
                                .accessDeniedHandler(accessDeniedHandler) // 授权失败处理
                )
                .sessionManagement(
                        httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer // session 管理
                                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 关闭 session
                )
                .authorizeHttpRequests(
                        auth -> auth // 请求匹配器
                                .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 允许所有 OPTIONS 请求
                                .requestMatchers("/login/user").permitAll() // 允许用户登录
                                .requestMatchers("/hello/admin").hasAuthority("system:test:list")
                                .anyRequest().authenticated() // 其他请求都需要认证
                )
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加过滤器
        return http.build();
    }

}